Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Ciao!
Ho un problema nel convertire un array di byte in un semplice oggetto... Ad esempio ho una classe di questo tipo
Codice sorgente - presumibilmente C#
publicclass Ciao{
publicbyte v1;
publicint v2, v3;
}
e il codice che tento di fare è un qualcosa di molto simile:
Codice sorgente - presumibilmente C# / VB.NET
Ciao ciao;
FileStream file = new FileStream("file.bin", FileMode.Open);
byte[] data = new data[9];
file.Read(data, 0, data.Length);
ciao = (Ciao)data; // La parte che ovviamente non mi funziona
Ad esempio nella classe Ciao ho un byte e due int, con un totale di 9 bytes, quindi ho bisogno di leggere i 9 byte dal file in lettura e assegnarlo completamente alla classe ciao. Lo sò che potrei fare tipo ciao.v1 = file.ReadByte() però dato che devo gestire moltissimi dati (sarà 3.9kb in totale) verrebbe un codice troppo lungo e poco performante. Consigli? Aiuti? Dritte?
noooo!!! avevo scritto un post molto dettagliato ma ho perso tutto perchè mi è scaduta la sessione di login... X_X
sono troppo pigro per riscriverlo per intero con la stessa cura XD
succo del discorso: il compilatore per ottimizzare la rapidità degli accessi alla memoria, riordina i membri di una classe o di una struct, sposta gli offset dei membri, in modo tale da renderli multipli dell'unità di memorizzazione di base (che dipende dall'architettura, generalmente 4 bytes su architetture a 32bit), e inserisce dei byte di padding (inutilizzati) nei buchi rimasti dentro la struct/classe.
Quindi i dati che leggi da file oltre ad avere una dimensione inferiore a quella della classe (a causa dei bytes di padding), hanno anche un ordine diverso rispetto a quello che hai specificato tu nel sorgente.
Il modo più semplice ed efficiente per ovviare al problema è quello di anteporre alla dichiarazione della struct/classe l'attributo __packed (questo su GCC; non ricordo l'equivalente per i compilatori microsoft). In questo modo il compilatore sa che deve mantenere la struct/classe nella forma esatta del sorgente.
Uffi peccato, una risposta più approfondita mi sarebbe piaciuta di più XD. Cmq il concetto l'ho capito . Ho fatto una breve ricerca su internet e purtroppo non ho trovato niente su una cosa equivalente al package sul C#... A questo punto mi dovrei creare una libreria in C che svolge questa funzione e poi richiamarla dal mio programma in C# giusto? Ma non ci sono sistemi più semplici? Meno codice ingombro nel mio progetto meglio è
Ad ogni modo, se in C questo è possibile, in .NET no. Anche ponendo quell'attributo non fai altro che riordinare i membri, ma rimane il fatto che non puoi convertire un array di byte in una struttura (o classe).
Per poterlo fare, devi definire una nuova versione dell'operatore CType per la tua struttura:
http://totem.altervista.org/guida/versione3/A27%20-%20Gli% ...
Ma anche questo è meno efficiente della serializzazione. Questa tecnica ti permette di convertire qualsiasi tipo di dato in uno stream di dati binari, e di riconvertirli in oggetto successivamente usando non più di cinque o sei righe di codice:
http://totem.altervista.org/guida/versione2/C14.php
Questi due metodi generics che ho scritto in un vecchio progetto possono salvare e caricare qualsiasi tipo di oggetto:
Codice sorgente - presumibilmente VB.NET
''' <summary>
''' Serializza qualsiasi oggetto.
''' </summary>
''' <typeparam name="T">Un qualsiasi tipo serializzabile.</typeparam>
''' <param name="File">Nome del file su quale serializzare l'oggetto.</param>
''' <param name="Graph">Oggetto da serializzare.</param>
''' <remarks>Se il file esiste, viene sovrascritto.</remarks>
PublicSub Serialize(Of T)(ByValFileAsString, ByVal Graph As T)
Dim Serializer AsNew Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Dim Stream AsNew IO.FileStream(File, IO.FileMode.Create)
Serializer.Serialize(Stream, Graph)
Stream.Close()
Serializer =Nothing
EndSub
''' <summary>
''' Deserializza qualsiasi oggetto.
''' </summary>
''' <typeparam name="T">Un qualsiasi tipo serializzabile.</typeparam>
''' <param name="File">Nome del file da cui deserializzare l'oggetto.</param>
''' <returns>Se il file non esiste, restituisce Nothing.</returns>
PublicFunction Deserialize(Of T)(ByValFileAsString)As T
Dim Serializer AsNew Runtime.Serialization.Formatters.Binary.BinaryFormatter()
Try
Dim Stream AsNew IO.FileStream(File, IO.FileMode.Open)
Dim Result As T
Result = Serializer.Deserialize(Stream)
Stream.Close()
Serializer =Nothing
Return Result
Catch Ex As Exception
Finally
Serializer =Nothing
EndTry
ReturnNothing
EndFunction
Ovviamente, se devi salvare più oggetti serializzerai una lista o un array di oggetti.
Questo è il codice... Funziona, ma il problema è che la srruttura non me la tocca proprio =( cioè che dopo questa operazione, v1 rimane uguale ad 1 e gli altri rimangono uguali a 0...
Certo, il marshalling è permesso con le strutture. Non l'ho scritto perchè pensavo ti bastassero tutti gli altri metodi.
Ma i valori li hai controllati in ciao? Perchè se è così, è normale: la funzione StructureToPtr copia solamente il contenuto di ciao nella memoria non gestita iniziante in ptr. Se poi gli copi dentro altri valori, è sempre quella memoria non gestita ad essere modificata. Quindi per ottenere la struttura modificata devi tornare indietro con PtrToStructure: